package my.edu.model.bills.fund.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import my.edu.common.constant.MessageConstant;
import my.edu.common.constant.OrderConstant;
import my.edu.common.domain.PageDTO;
import my.edu.common.domain.PageVO;
import my.edu.common.exception.BillsException;
import my.edu.common.utils.PageUtils;
import my.edu.constant.BillsType;
import my.edu.constant.FoundBussinessType;
import my.edu.constant.OrderNoConstant;
import my.edu.model.bills.fund.entity.OtherIncomeOrderDetail;
import my.edu.model.bills.fund.entity.dto.InComeDetailDTO;
import my.edu.model.bills.fund.entity.dto.IncomeDTO;
import my.edu.model.bills.fund.entity.dto.IncomeSearchDTO;
import my.edu.model.bills.fund.entity.vo.IncomeDetailVO;
import my.edu.model.bills.fund.entity.vo.IncomeVO;
import my.edu.model.information.service.IAccountService;
import my.edu.utils.ChildrenLoader;
import my.edu.utils.OrderNoUtil;
import my.edu.utils.OrderServiceUtil;
import my.edu.websocket.BillWebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import my.edu.model.bills.fund.entity.OtherIncomeOrder;
import my.edu.model.bills.fund.mapper.OtherIncomeOrderMapper;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Service
public class OtherIncomeOrderServiceImpl extends ServiceImpl<OtherIncomeOrderMapper, OtherIncomeOrder> implements IOtherIncomeOrderService {

    @Autowired
    private OrderNoConstant orderNoConstant;
    @Autowired
    private IOtherIncomeOrderDetailService otherIncomeOrderDetailService;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private OrderServiceUtil orderServiceUtil;
    @Autowired
    private FoundBussinessType businessType;
    @Autowired
    private BillWebSocket billWebSocket;

    @Override
    public void saveNew(IncomeDTO incomeDTO) {
        checkDTO(incomeDTO, true);
        OtherIncomeOrder otherIncomeOrder = BeanUtil.copyProperties(incomeDTO, OtherIncomeOrder.class, "incomeDetailDTOList");
        setField(otherIncomeOrder, incomeDTO.getIncomeDetailDTOList());
        save(otherIncomeOrder);
        otherIncomeOrderDetailService.saveBatchByDTO(incomeDTO.getIncomeDetailDTOList(), otherIncomeOrder);
        billWebSocket.sendToAllClient(BillsType.OTHERINCOME,"单据类型：收入单;单据编号："+otherIncomeOrder.getOrderNo()+";提交人："+otherIncomeOrder.getCreatedName());

    }

    private void setField(OtherIncomeOrder otherIncomeOrder, List<InComeDetailDTO> incomeDetailDTOList) {
        BigDecimal amount = BigDecimal.ZERO;
        for (InComeDetailDTO inComeDetailDTO : incomeDetailDTOList) {
            amount = amount.add(inComeDetailDTO.getAmount());
        }
        BigDecimal debt = amount.subtract(otherIncomeOrder.getAmountReceived());
        otherIncomeOrder.setReceiptAmount(amount);
        otherIncomeOrder.setDebtAmount(debt);
        if (debt.compareTo(BigDecimal.ZERO) == 0){
            otherIncomeOrder.setReceiptStatus(OrderConstant.GET_PAYMENT);
            otherIncomeOrder.setReceiptDate(LocalDate.now());
        }
        else if (debt.compareTo(amount) == 0) {
            otherIncomeOrder.setReceiptStatus(OrderConstant.NOT_PAYMENT);
            otherIncomeOrder.setReceiptDate(null);
        }
        else{
            otherIncomeOrder.setReceiptStatus(OrderConstant.PART_PAYMENT);
            otherIncomeOrder.setReceiptDate(null);
        }
        otherIncomeOrder.setIsAudited(0);
    }

    private void checkDTO(IncomeDTO incomeDTO, boolean isNew) {
        if (incomeDTO.getOrderNo() == null || incomeDTO.getOrderNo().isEmpty()) {
            //设置No
            String orderNo = OrderNoUtil.generateOrderNo(orderNoConstant.getOtherIncomeNo());
            incomeDTO.setOrderNo(orderNo);
        } else {
            //检查是否有重复no
            long count = count(Wrappers.<OtherIncomeOrder>lambdaQuery().eq(OtherIncomeOrder::getOrderNo, incomeDTO.getOrderNo()));
            if (isNew && count > 0) throw new BillsException(MessageConstant.ORDER_NO_EXISTS);
        }
        orderServiceUtil.checkDTO(null, incomeDTO.getSettlementAccountId(), incomeDTO.getSalespersonId(), null, incomeDTO.getCustomerId());
        List<String> boundBusinessType = businessType.getIncomeBusinessType();
        for (InComeDetailDTO inComeDetailDTO : incomeDTO.getIncomeDetailDTOList()) {
            boolean contains = boundBusinessType.contains(inComeDetailDTO.getIncomeCategory());
            if (!contains) throw new BillsException(MessageConstant.BOUND_BUSINESS_TYPE_NOT_FOUND);
        }
    }

    @Override
    public void updateByDTO(IncomeDTO incomeDTO) {
        OtherIncomeOrder byId = getById(incomeDTO.getId());
        if (byId == null) throw new BillsException(MessageConstant.ORDER_NOT_EXIST);
        if (byId.getIsAudited() != null && byId.getIsAudited() == 1)
            throw new BillsException(MessageConstant.ISAUDIT_CAN_NOT_UPDATE);
        checkDTO(incomeDTO, false);
        BeanUtil.copyProperties(incomeDTO, byId, "incomeDetailDTOList");
        setField(byId, incomeDTO.getIncomeDetailDTOList());
        updateById(byId);
        otherIncomeOrderDetailService.updateByDTO(incomeDTO.getIncomeDetailDTOList(), byId);
        billWebSocket.sendToAllClient(BillsType.OTHERINCOME,"单据类型：收入单;单据编号："+byId.getOrderNo()+";提交人："+byId.getUpdateName());

    }

    @Override
    @Transactional
    public void removeOrder(List<Long> longs) {
        List<OtherIncomeOrder> otherIncomeOrders = listByIds(longs);
        for (OtherIncomeOrder otherIncomeOrder : otherIncomeOrders) {
            if (otherIncomeOrder.getIsAudited() != null && otherIncomeOrder.getIsAudited() == 1)
                throw new BillsException(otherIncomeOrder.getOrderNo() + MessageConstant.ISAUDIT_CAN_NOT_DEL);
        }
        removeBatchByIds(longs);
        otherIncomeOrderDetailService.removeByOrderId(longs);
    }

    @Override
    public void auditOrder(List<Long> longs) {
        updateAudit(longs, true);
    }

    private void updateAudit(List<Long> longs, boolean isAudit) {
        List<OtherIncomeOrder> list = list(Wrappers.<OtherIncomeOrder>lambdaQuery().in(OtherIncomeOrder::getId, longs));
        for (OtherIncomeOrder order : list) {
            if (isAudit) {
                if (order.getIsAudited() != null && order.getIsAudited() == 1)
                    throw new BillsException(order.getOrderNo() + MessageConstant.ISAUDITED);
            } else if (order.getIsAudited() != null && order.getIsAudited() == 0) {
                throw new BillsException(order.getOrderNo() + MessageConstant.NOTAUDITED);
            }
        }
        Map<Long, BigDecimal> map = getAccountIdToTotalAmountMap(list);
        if (isAudit) {
            orderServiceUtil.setAuditStatus(this, longs, true);
            accountService.addBalance(map);
        } else {
            orderServiceUtil.setAuditStatus(this, longs, false);
            accountService.subBalance(map);
        }
    }

    private Map<Long, BigDecimal> getAccountIdToTotalAmountMap(List<OtherIncomeOrder> list) {
        return list.stream()
                .collect(Collectors.toMap(
                        OtherIncomeOrder::getSettlementAccountId,
                        i -> i.getAmountReceived()
                ));
    }

    @Override
    public void rauditOrder(List<Long> longs) {
        updateAudit(longs, false);
    }

    @Override
    public PageVO<IncomeVO> getOrderList(PageDTO page, IncomeSearchDTO incomeSearchDTO) {
        MPJLambdaWrapper<OtherIncomeOrder> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(OtherIncomeOrder.class, IncomeVO.class)
                .like(incomeSearchDTO.getOrderNo() != null && !incomeSearchDTO.getOrderNo().isEmpty(),
                        OtherIncomeOrder::getOrderNo, incomeSearchDTO.getOrderNo())
                .between(incomeSearchDTO.getBeginTime() != null,
                        OtherIncomeOrder::getOrderDate, incomeSearchDTO.getBeginTime(), incomeSearchDTO.getEndTime())
                .eq(incomeSearchDTO.getIsAudited() != null,
                        OtherIncomeOrder::getIsAudited, incomeSearchDTO.getIsAudited())
                .eq(incomeSearchDTO.getSalesmanId() != null,
                        OtherIncomeOrder::getSalespersonId, incomeSearchDTO.getSalesmanId())
                .eq(incomeSearchDTO.getAuditorId() != null,
                        OtherIncomeOrder::getAuditorId, incomeSearchDTO.getAuditorId())
                .eq(incomeSearchDTO.getSettlementAccountId() != null,
                        OtherIncomeOrder::getSettlementAccountId, incomeSearchDTO.getSettlementAccountId())
                .eq(incomeSearchDTO.getPaymentStatus() != null,
                        OtherIncomeOrder::getReceiptStatus, incomeSearchDTO.getPaymentStatus())
                .orderByDesc(OtherIncomeOrder::getCreateTime);
        Page<IncomeVO> incomeVOPage = baseMapper.selectJoinPage(new Page<>(page.getPage(), page.getSize()), IncomeVO.class, wrapper);
//        List<CompletableFuture<Void>> futures = new ArrayList<>();
//        for (IncomeVO record : incomeVOPage.getRecords()) {
//            futures.add(CompletableFuture.runAsync(() -> setVoDetail(record)));
//        }
//        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        if (CollUtil.isNotEmpty(incomeVOPage.getRecords())) {
            ChildrenLoader.loadAndFillDTO(
                    incomeVOPage.getRecords(),
                    IncomeVO::getId,
                    ids -> otherIncomeOrderDetailService.list(
                            Wrappers.<OtherIncomeOrderDetail>lambdaQuery().in(OtherIncomeOrderDetail::getOtherIncomeOrderId, ids)),
                    IncomeDetailVO::getOtherIncomeOrderId,
                    IncomeDetailVO.class,
                    IncomeVO::setIncomeDetailVOList);
        };
        return PageUtils.toPage(incomeVOPage);
    }

//    @Async
//    protected void setVoDetail(IncomeVO record) {
//        List<OtherIncomeOrderDetail> list = otherIncomeOrderDetailService.list(Wrappers.<OtherIncomeOrderDetail>lambdaQuery().eq(OtherIncomeOrderDetail::getOtherIncomeOrderId, record.getId()));
//        List<IncomeDetailVO> incomeDetailVOS = BeanUtil.copyToList(list,IncomeDetailVO.class);
//        record.setIncomeDetailVOList(incomeDetailVOS);
//    }

    @Override
    public Object getOrderOne(Long id) {
        MPJLambdaWrapper<OtherIncomeOrder> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(OtherIncomeOrder.class, IncomeVO.class)
                .selectCollection(OtherIncomeOrderDetail.class, IncomeVO::getIncomeDetailVOList)
                .leftJoin(OtherIncomeOrderDetail.class, OtherIncomeOrderDetail::getOtherIncomeOrderId, OtherIncomeOrder::getId)
                .eq(OtherIncomeOrder::getId, id);
        return baseMapper.selectJoinOne(IncomeVO.class, wrapper);
    }
}
